home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue40 / COMCorn / Main.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-11-10  |  11.0 KB  |  356 lines

  1. unit Main;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Classes, ActiveX, ComObj, ShlObj, UtilObjs;
  7.  
  8. type
  9.   TComNameExt = class(TComObject, IShellFolder, IPersistFolder,
  10.     IEnumIDList)
  11.   private
  12.     FServList: TComServerList;
  13.     FShellMalloc: IMalloc;
  14.     property ServList: TComServerList read FServList write FServList implements IEnumIDList;
  15.   protected
  16.     { Method aliases }
  17.     function IPersistFolder.Initialize = PersistFolderInitialize;
  18.     { IShellFolder methods }
  19.     function ParseDisplayName(hwndOwner: HWND;
  20.       pbcReserved: Pointer; lpszDisplayName: POLESTR; out pchEaten: ULONG;
  21.       out ppidl: PItemIDList; var dwAttributes: ULONG): HResult; stdcall;
  22.     function EnumObjects(hwndOwner: HWND; grfFlags: DWORD;
  23.       out EnumIDList: IEnumIDList): HResult; stdcall;
  24.     function BindToObject(pidl: PItemIDList; pbcReserved: Pointer;
  25.       const riid: TIID; out ppvOut: Pointer): HResult; stdcall;
  26.     function BindToStorage(pidl: PItemIDList; pbcReserved: Pointer;
  27.       const riid: TIID; out ppvObj: Pointer): HResult; stdcall;
  28.     function CompareIDs(lParam: LPARAM;
  29.       pidl1, pidl2: PItemIDList): HResult; stdcall;
  30.     function CreateViewObject(hwndOwner: HWND; const riid: TIID;
  31.       out ppvOut: Pointer): HResult; stdcall;
  32.     function GetAttributesOf(cidl: UINT; var apidl: PItemIDList;
  33.       var rgfInOut: UINT): HResult; stdcall;
  34.     function GetUIObjectOf(hwndOwner: HWND; cidl: UINT; var apidl: PItemIDList;
  35.       const riid: TIID; prgfInOut: Pointer; out ppvOut: Pointer): HResult; stdcall;
  36.     function GetDisplayNameOf(pidl: PItemIDList; uFlags: DWORD;
  37.       var lpName: TStrRet): HResult; stdcall;
  38.     function SetNameOf(hwndOwner: HWND; pidl: PItemIDList; lpszName: POLEStr;
  39.       uFlags: DWORD; var ppidlOut: PItemIDList): HResult; stdcall;
  40.     { IPersist method }
  41.     function GetClassID(out classID: TCLSID): HResult; stdcall;
  42.     { IPersistFolder method }
  43.     function PersistFolderInitialize(pidl: PItemIDList): HResult; stdcall;
  44.   public
  45.     destructor Destroy; override;
  46.     procedure Initialize; override;
  47.     procedure RefreshServerList; virtual;
  48.     property ShellMalloc: IMalloc read FShellMalloc;
  49.   end;
  50.  
  51.   TNamespaceExtensionFactory = class(TComObjectFactory)
  52.   protected
  53.     function GetProgID: string; override;
  54.   public
  55.     procedure UpdateRegistry(Register: Boolean); override;
  56.   end;
  57.  
  58. const
  59.   Class_ComNameExt: TGUID = '{6CA52791-76C1-11D2-8BFB-00104B700B61}';
  60.  
  61. implementation
  62.  
  63. uses ComServ, SysUtils, Registry, ViewObj, UIObjs;
  64.  
  65. { TComNameExt }
  66.  
  67. destructor TComNameExt.Destroy;
  68. begin
  69.   FServList.Free;
  70.   inherited Destroy;
  71. end;
  72.  
  73. procedure TComNameExt.Initialize;
  74. begin
  75.   inherited Initialize;
  76.   FServList := TComServerList.Create(Self);
  77.   OleCheck(ShGetMalloc(FShellMalloc));    // Squirrel away shell's IMalloc
  78. end;
  79.  
  80. procedure TComNameExt.RefreshServerList;
  81. var
  82.   Reg: TRegistry;
  83.   I, KeyIdx: Integer;
  84.   ClsidKeys, SubKeys: TStringList;
  85.   CurrentKey: string;
  86. begin
  87.   // This method just reads through the HKEY_CLASSES_ROOT\CLSID registry key,
  88.   // and saves all the COM servers listed under that key to the ServList list.
  89.   Reg := TRegistry.Create;
  90.   ClsidKeys := TStringList.Create;
  91.   try
  92.     ServList.Clear;
  93.     Reg.RootKey := HKEY_CLASSES_ROOT;
  94.     if not Reg.OpenKeyReadOnly('CLSID') then
  95.       raise Exception.Create('Failed to open registry');
  96.     Reg.GetKeyNames(ClsidKeys);  // Get list of registered CLSIDs
  97.     Reg.CloseKey;
  98.     SubKeys := TStringList.Create;
  99.     try
  100.       for I := 0 to ClsidKeys.Count - 1 do  // iterate over all CLSIDs
  101.       begin
  102.         CurrentKey := ClsidKeys[I];
  103.         if Reg.OpenKeyReadOnly('CLSID\' + CurrentKey) then
  104.         begin
  105.           // Make sure this CLSID is a COM server
  106.           Reg.GetKeyNames(SubKeys);
  107.           Reg.CloseKey;
  108.           KeyIdx := SubKeys.IndexOf('InprocServer32');
  109.           if KeyIdx < 0 then KeyIdx := SubKeys.IndexOf('LocalServer32');
  110.           if KeyIdx < 0 then Continue;
  111.           // Add CLSID to list of COM servers
  112.           ServList.AddGuid(StringToGUID(CurrentKey));
  113.         end;
  114.       end;
  115.     finally
  116.       SubKeys.Free;
  117.     end;
  118.   finally
  119.     Reg.Free;
  120.     ClsidKeys.Free;
  121.   end;
  122. end;
  123.  
  124. { TComNameExt.IShellFolder }
  125.  
  126. function TComNameExt.ParseDisplayName(hwndOwner: HWND;
  127.   pbcReserved: Pointer; lpszDisplayName: POLESTR; out pchEaten: ULONG;
  128.   out ppidl: PItemIDList; var dwAttributes: ULONG): HResult;
  129. begin
  130.   Result := E_NOTIMPL;
  131. end;
  132.  
  133. function TComNameExt.EnumObjects(hwndOwner: HWND; grfFlags: DWORD;
  134.   out EnumIDList: IEnumIDList): HResult;
  135. begin
  136.   Result := S_OK;
  137.   try
  138.     // IEnumIDList implementation methods live in a separate object just
  139.     // because I think that is cleaner. 
  140.     EnumIDList := Self as IEnumIDList;
  141.   except
  142.     on E: TObject do
  143.       Result := SafeCallException(E, ExceptAddr);
  144.   end;
  145. end;
  146.  
  147. function TComNameExt.BindToObject(pidl: PItemIDList; pbcReserved: Pointer;
  148.   const riid: TIID; out ppvOut: Pointer): HResult;
  149. begin
  150.   Result := E_NOTIMPL;
  151. end;
  152.  
  153. function TComNameExt.BindToStorage(pidl: PItemIDList; pbcReserved: Pointer;
  154.   const riid: TIID; out ppvObj: Pointer): HResult;
  155. begin
  156.   Result := E_NOTIMPL;
  157. end;
  158.  
  159. function TComNameExt.CompareIDs(lParam: LPARAM;
  160.   pidl1, pidl2: PItemIDList): HResult;
  161. var
  162.   Guids1, Guids2: string;
  163.   IDs1, IDs2: PServInfo;
  164. begin
  165.   // This method does a reasonably cheesey compare of two pidls simply by
  166.   // converting the CLSID portion of my PIDL to a string and using CompareStr.
  167.   try
  168.     IDs1 := PServInfo(pidl1);
  169.     IDs2 := PServInfo(pidl2);
  170.     while IDs1.Size <> 0 do
  171.     begin
  172.       Guids1 := Guids1 + GuidToString(IDs1^.CLSID);
  173.       Inc(IDs1);
  174.     end;
  175.     while IDs2.Size <> 0 do
  176.     begin
  177.       Guids1 := Guids1 + GuidToString(IDs2^.CLSID);
  178.       Inc(IDs2);
  179.     end;
  180.     Result := CompareStr(Guids1, Guids2);  // sort alphabetically
  181.   except
  182.     on E: TObject do
  183.       Result := SafeCallException(E, ExceptAddr);
  184.   end;
  185. end;
  186.  
  187. function TComNameExt.CreateViewObject(hwndOwner: HWND; const riid: TIID;
  188.   out ppvOut: Pointer): HResult;
  189. begin
  190.   // Return an IShellView pointer when requested.  Remember that this method
  191.   // Can be called many times, so this class must support multiple IShellViews
  192.   // on a single IShellFolder.
  193.   Result := S_OK;
  194.   try
  195.     ppvOut := nil;
  196.     if IsEqualGuid(riid, IShellView) then
  197.       IShellView(ppvOut) := TViewObject.Create(Self, hwndOwner)
  198.     else
  199.       Result := E_NOINTERFACE;
  200.   except
  201.     on E: TObject do
  202.       Result := SafeCallException(E, ExceptAddr);
  203.   end;
  204. end;
  205.  
  206. function TComNameExt.GetAttributesOf(cidl: UINT; var apidl: PItemIDList;
  207.   var rgfInOut: UINT): HResult;
  208. begin
  209.   if (@rgfInOut = nil) then
  210.   begin
  211.     Result := E_POINTER;
  212.     Exit;
  213.   end;
  214.   rgfInOut := 0;  // pretty boring...
  215.   Result := S_OK;
  216. end;
  217.  
  218. function TComNameExt.GetUIObjectOf(hwndOwner: HWND; cidl: UINT; var apidl: PItemIDList;
  219.   const riid: TIID; prgfInOut: Pointer; out ppvOut: Pointer): HResult;
  220. var
  221.   Guids: array of TGUID;
  222.   ServInfo: PServInfo;
  223.   I: Integer;
  224. begin
  225.   // Returns an IContextMenu or IExtractIcon when requested.  Like IShellViews,
  226.   // this IShellFolder must support multiple UI objects at one time.
  227.   Result := S_OK;
  228.   ppvOut := nil;
  229.   try
  230.     SetLength(Guids, cidl);
  231.     ServInfo := PServInfo(apidl);
  232.     // Store the GUIDs for each pidl in an array
  233.     for I := 0 to cidl - 1 do
  234.     begin
  235.       Guids[I] := ServInfo.CLSID;
  236.       Inc(ServInfo);
  237.     end;
  238.     if IsEqualGuid(riid, IContextMenu) then
  239.     begin
  240.        IContextMenu(ppvOut) := TContextMenu.Create(Self, hwndOwner, Guids);
  241.     end
  242.     else if IsEqualGuid(riid, IExtractIcon) then
  243.     begin
  244.        IExtractIcon(ppvOut) := TExtractIcon.Create(Self, hwndOwner, Guids[0]);
  245.     end
  246.     else begin
  247.       Result := E_NOINTERFACE;
  248.       Exit;
  249.     end;
  250.   except
  251.     on E: TObject do
  252.       Result := SafeCallException(E, ExceptAddr);
  253.   end;
  254. end;
  255.  
  256. function TComNameExt.GetDisplayNameOf(pidl: PItemIDList; uFlags: DWORD;
  257.   var lpName: TStrRet): HResult;
  258. var
  259.   Guid: TGUID;
  260.   NameStr, GuidStr: string;
  261.   Reg: TRegistry;
  262. begin
  263.   // Looks to the registry to find the name of a particular COM server
  264.   Result := S_OK;
  265.   try
  266.     FillChar(lpName, SizeOf(lpName), 0);
  267.     Guid := PServInfo(pidl)^.CLSID;
  268.     GuidStr := GuidToString(Guid);
  269.     lpName.uType := STRRET_CSTR;
  270.     if HiWord(uFlags) and (SHGDN_FORPARSING) = 0 then
  271.     begin
  272.       Reg := TRegistry.Create;
  273.       Reg.RootKey := HKEY_CLASSES_ROOT;
  274.       Reg.OpenKeyReadOnly('CLSID\' + GuidStr);
  275.       NameStr := Reg.ReadString('');
  276.     end;
  277.     if NameStr = '' then NameStr := GuidStr;
  278.     StrLCopy(lpName.cStr, PChar(NameStr), SizeOf(lpName.cStr));
  279.   except
  280.     on E: TObject do
  281.       Result := SafeCallException(E, ExceptAddr);
  282.   end;
  283. end;
  284.  
  285. function TComNameExt.SetNameOf(hwndOwner: HWND; pidl: PItemIDList; lpszName: POLEStr;
  286.   uFlags: DWORD; var ppidlOut: PItemIDList): HResult;
  287. begin
  288.   Result := E_NOTIMPL;
  289. end;
  290.  
  291. { TComNameExt.IPersist }
  292.  
  293. function TComNameExt.GetClassID(out classID: TCLSID): HResult;
  294. begin
  295.   classID := Class_ComNameExt;
  296.   Result := S_OK;
  297. end;
  298.  
  299. { TComNameExt.IPersistFolder }
  300.  
  301. function TComNameExt.PersistFolderInitialize(pidl: PItemIDList): HResult;
  302. begin
  303.   Result := S_OK;  // Don't care where we where initialized
  304. end;
  305.  
  306. { TNamespaceExtensionFactory }
  307.  
  308. function TNamespaceExtensionFactory.GetProgID: string;
  309. begin
  310.   // ProgID not required for namespace extension
  311.   Result := '';
  312. end;
  313.  
  314. procedure TNamespaceExtensionFactory.UpdateRegistry(Register: Boolean);
  315. const
  316.   NamespaceKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\' +
  317.     'MyComputer\Namespace\';
  318.   ApproveKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\';
  319. var
  320.   ClsID: string;
  321.   Value: DWORD;
  322. begin
  323.   ClsID := GUIDToString(Class_ComNameExt);
  324.   inherited UpdateRegistry(Register);
  325.   if Register then
  326.   begin
  327.     // Junction for this amespace extension is "My Computer"
  328.     CreateRegKeyEx(NameSpaceKey + ClsId, '', PChar(Description), REG_SZ,
  329.       Length(Description) + 1, HKEY_LOCAL_MACHINE);
  330.     // Shell extension must be approved under NT
  331.     if Win32Platform = VER_PLATFORM_WIN32_NT then
  332.       CreateRegKeyEx(ApproveKey, ClsId, PChar(Description), REG_SZ,
  333.         Length(Description) + 1, HKEY_LOCAL_MACHINE);
  334.     // Write "Folder" attribute to registry
  335.     Value := SFGAO_FOLDER;
  336.     CreateRegKeyEx('CLSID\' + ClsId + '\ShellFolder\', 'Attributes',
  337.       @Value, REG_BINARY, SizeOf(DWORD), HKEY_CLASSES_ROOT);
  338.     // Default icon is contained within this file
  339.     CreateRegKey('CLSID\' + ClsId + '\DefaultIcon', '',
  340.       ComServer.ServerFileName + ',0');
  341.   end
  342.   else begin
  343.     // Remove junction
  344.     RegDeleteKey(HKEY_LOCAL_MACHINE, PChar(NameSpaceKey + ClsId));
  345.     // Remove approval on NT
  346.     if Win32Platform = VER_PLATFORM_WIN32_NT then
  347.       DeleteRegValue(ApproveKey, ClsId, HKEY_LOCAL_MACHINE);
  348.   end;
  349. end;
  350.  
  351. initialization
  352.   TNamespaceExtensionFactory.Create(ComServer, TComNameExt, Class_ComNameExt,
  353.     'ComNameExt', 'Registered COM objects', ciMultiInstance,
  354.     tmApartment);
  355. end.
  356.